﻿using System.ComponentModel;
using SS.WPF.Framework.Restriction;

namespace SS.WPF.Toolkit.PropertyEditor
{
    using System.Collections.Generic;

    /// <summary>
    /// The class is used to define a collection of properties
    /// </summary>
    public class PropertyCollection: CompositeItem
    {
        #region Fields

        private bool _useCustomTypeConverter;

        /// <summary>
        /// Refer to Property source
        /// </summary>
        private readonly object _propertySource;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Create new instance of PropertyCollection with specified inputs
        /// </summary>
        /// <param name="propertySource">Refer to property source</param>
        /// <param name="filter">Criteria to filter</param>
        public PropertyCollection(object propertySource, string filter)
        {
            this._propertySource = propertySource;
            var properties = this.GetProperties();
            this.UpdateItems(properties, filter);
        }

        #endregion

        #region Help methods

        /// <summary>
        /// Get Properties from PropertySource
        /// </summary>
        private PropertyDescriptorCollection GetProperties()
        {
            PropertyDescriptorCollection properties = null;
            if (this._propertySource != null)
            {
                var tc = TypeDescriptor.GetConverter(this._propertySource);
                if (!tc.GetPropertiesSupported())
                {
                    if (this._propertySource is ICustomTypeDescriptor)
                    {
                        properties = ((ICustomTypeDescriptor)this._propertySource).GetProperties();
                    }
                    else
                    {
                        properties = TypeDescriptor.GetProperties(this._propertySource.GetType());
                    }
                }
                else
                {
                    properties = tc.GetProperties(this._propertySource);
                    _useCustomTypeConverter = true;
                }
            }

            return properties ?? new PropertyDescriptorCollection(new PropertyDescriptor[] {});
        }

        /// <summary>
        /// Update Items from PropertyDescriptorCollection
        /// </summary>
        /// <param name="properties"></param>
        /// <param name="filter"></param>
        private void UpdateItems(PropertyDescriptorCollection properties, string filter)
        {
            var propertyCollection = new List<Property>();

            foreach (PropertyDescriptor propertyDescriptor in properties)
            {
                if (_useCustomTypeConverter)
                {
                    var property = new Property(this._propertySource, propertyDescriptor);

                    SetRestriction(propertyDescriptor, property);

                    propertyCollection.Add(property);
                }
                else
                {
                    this.CollectProperties(this._propertySource, propertyDescriptor, propertyCollection);
                    propertyCollection.Sort(Property.CompareByCategoryThenByName);
                }
            }

            foreach (Property property in propertyCollection)
            {
                if (filter == string.Empty || property.Name.ToLower().Contains(filter.Trim().ToLower()))
                {
                    this.Items.Add(property);
                }
            }
        }

        /// <summary>
        /// Update Restriction for Property from instance of PropertyDescriptor
        /// </summary>
        /// <param name="propertyDescriptor"></param>
        /// <param name="property"></param>
        private void SetRestriction(PropertyDescriptor propertyDescriptor, Property property)
        {
            var restrictionContainer = propertyDescriptor as IRestrictionContainer;
            if (restrictionContainer != null)
            {
                property.Restriction = restrictionContainer.Restriction;
            }

            var descriptor = propertyDescriptor as IBindingGroup;
            if (descriptor != null)
            {
                property.ValidationGroup = descriptor.ValidationGroup;
            }
        }

        private void CollectProperties(object propertySource, PropertyDescriptor descriptor, List<Property> propertyCollection)
        {
            var property = new Property(propertySource, descriptor);

            SetRestriction(descriptor, property);

            if (descriptor.IsBrowsable)
            {
                propertyCollection.Add(property);
            }
        }

        #endregion
    }
}
